/* 
 * File:   SupportVectorMachine.cpp
 * Author: ungerma
 * 
 * Created on 04. August 2014, 15:30
 */

#include "SupportVectorMachine.h"

using namespace std;

SupportVectorMachine::SupportVectorMachine() {
    this->settings = HOGSettings();
}

SupportVectorMachine::SupportVectorMachine(HOGSettings settings) {
    this->settings = settings;
    
}

void SupportVectorMachine::train(string exe, string featureFile, 
        string svmFile, string svmParameters) {
    
    string cmd = exe +  " " + svmParameters + " " + featureFile + " " + svmFile;
    cout << cmd << endl;
    int i = system(cmd.c_str());
    
    if(i != 0) {
        throw runtime_error("Could not run training command");
    }
    
}

TestResults SupportVectorMachine::classify(string exe, string featureFile, 
        string modelFile, string clzFile, Images& images) {
    
    TestResults results;
    string cmd = exe +  " " + featureFile + " " + modelFile + " " + clzFile;
    cout << cmd << endl;
    int r = system(cmd.c_str());
    
    if(r != 0) {
        throw runtime_error("Could not run classification command");
    }
    
    ifstream file;    
    file.open(clzFile.c_str());
    if(!file.is_open()) {
        throw runtime_error("No classification result file found " 
                + clzFile);
    }
    
    readNextLines(results, images.getPosTest(), TestResults::CLASS_A, file);
    readNextLines(results, images.getNegTest(), TestResults::CLASS_B, file);    
    
    file.close();
    if(file.is_open()) {
        throw runtime_error("Could not close result file");
    }
    
    return results;
    
}

void SupportVectorMachine::readNextLines(TestResults& results,
        vector<SingleImage> sis, float groundTruth, ifstream& file ) {
    
    float y, yscore;
    string line;
    
    for(int p = 0; p < sis.size(); ++p) {       
        SingleImage i = sis.at(p);
        yscore = Images::MIN_Y;
        if(i.hasWindows()) {
         
            string id = "";
            for(int w = 0; w < i.getWindows()->size(); ++w) {
                
                getline(file, line);
                y = atof(line.c_str());
                if(y > yscore) {
                    yscore = y;
                    id = i.getWindows()->at(w).getIDString();
                }
            
            }
            results.result(yscore, groundTruth,
                    yscore > Images::LIMIT ? TestResults::CLASS_A : 
                                             TestResults::CLASS_B  ,
                    id);
            
        }
        else {
            
            getline(file, line);
            y = atof(line.c_str());
            yscore = max(y, yscore);
            
            results.result(yscore, groundTruth, 
                    yscore > Images::LIMIT ? TestResults::CLASS_A : 
                                             TestResults::CLASS_B  ,
                    i.getIDString());  
            
        }
        
    }    
}

/*
 * Calculate the weights from SVM light output.
 * For linear kernels only!
 */
vector<float> SupportVectorMachine::deserializePrimalForm(string filename) {

    ifstream file;
    string line;
    float *w, y_alpha, xi, b;
    int fo, i;
    
    file.open(filename.c_str());
    if(!file.is_open()) {
         throw runtime_error("Could not open " + filename + " for writing");
    }
    
    // Skip first 7 lines.
    for(int n=0 ; n < 7 ; ++n) {
        getline(file, line);
    }
    
    // Read highest feature index.
    // Allocate memory for features + 1 for b.
    getline(file, line);
    fo = line.find_first_of(' ');
    int highestIndex = atof(line.substr(0, fo + 1).c_str());    
    w = new float[highestIndex + 1];
    for(int n=0; n<highestIndex + 1; ++n) {
        w[n] = 0;
    }
    
    // Skip 2 lines.
    getline(file, line);
    getline(file, line);
    
    // Read b.
    getline(file, line);    
    fo = line.find_first_of(' ');
    b = atof(line.substr(0, fo - 1).c_str());
    
    // Store b as last element.
    w[highestIndex] = b;
    
    // Read rest = features.
    while(file >> line) {
                
        // Read alpha_i * y_i.
        fo = line.find_first_of(':');   
        if(fo < 0) {
            y_alpha = atof(line.c_str());
        }
        else {
            // Unless comment.
            if(line != "#") {
                i = atof(line.substr(0, fo).c_str());
                xi = atof(line.substr(fo + 1).c_str()); 
                
                // Finally - calculate weight vector.
                w[i - 1] = w[i] + xi * y_alpha;
                
            }
        }    
    }
    
    file.close();
    if(file.is_open()) {
        throw runtime_error("Could not close " + filename);
    }
    
    vector<float> ret;
    ret.assign(w, w + highestIndex + 1);
    delete w;
    return ret;
}
